
/*
 * Compiled on 64-bit machine
 */
#include "problem_1.h"

void traverse_ready_list(void);

typedef struct _MyPCB {

    struct _MyPCB *next;
    struct _MyPCB *prev;
    struct PCB *proc;
    int time_remain;
} MyPCB;

MyPCB *waiting_list_head = NULL;
MyPCB *waiting_list_tail = NULL;



/*
 * if the argument is positive, block the current process for the
 * specified number of ticks
 *
 * if the argument is 0 or negative, do nothing
 */
void sys_sleep(int ticks) {

    if (ticks <= 0)
        return;

    int time_cur = get_current_time();      // current system time
    int wakeup_time = time_cur + ticks;     // current proc's wake-up time 
        
    char *pID_cur = current->id;
    
    
    /* do nothing if process is sleeping or is not running */
    if (current->state == READY || current->state == WAITING) 
        return;

    
    /* display message */
    //printf("---------------------------------------------------- \n");
    printf("SLEEP CODE: at time %d: putting %s to sleep until %d \n",
            time_cur, pID_cur, wakeup_time);

                
    /* set PCB's link field */    
    current->state = WAITING;

    // put current process at the tail of waiting list
    //
    // create a waiting list's tail
    MyPCB *temp = (MyPCB*)kmalloc(sizeof(MyPCB));
    temp->next = NULL;        
    temp->prev = NULL;
    temp->proc = current;
    temp->time_remain = ticks;


    if (waiting_list_tail == NULL)
    {                       
        waiting_list_tail = temp;               
        waiting_list_head = waiting_list_tail;
    }
    else {        
        /* put to end of waiting list */
        waiting_list_tail->next = temp;
        temp->prev = waiting_list_tail;        
        waiting_list_tail = temp;
    }    

    /* ask scheduler to find next ready process */
    dispatch();
}


/*
 * the OS calls this function once per tick; you use it to wake up
 * sleeping processes
 *
 * an awakened process should be put at the head of the ready list
 *
 * make sure you wake up all processes whose time is up, not just
 * one of them
 */
void timer_sleep(void) {

    // traverse waiting_list
    //
    if (waiting_list_head == NULL) {    
        return;
    }    
    else {
        // check if it's time to wake up
        MyPCB *me = waiting_list_head;

        while (me != NULL) {
            if (me->time_remain == 0)
            {
                // remove process from waiting list
                MyPCB *temp_next = me->next;
                MyPCB *temp_prev = me->prev;

                if (temp_next != NULL) {
                    temp_next->prev = temp_prev;                
                }
                if (temp_prev != NULL) {
                    temp_prev->next = temp_next;
                }
                
                if (me == waiting_list_head) {
                    waiting_list_head = me->next;
                }
                if (me == waiting_list_tail) {
                    waiting_list_tail = me->prev;
                }                              

                // set state to READY
                (me->proc)->state = READY;

                // add current PCB to head of ready list                
                if (ready_list != NULL)
                {
                    //traverse_ready_list();

                    (me->proc)->link = ready_list;
                    ready_list = me->proc;

                    //traverse_ready_list();

                }
                else {
                    // create new ready_list
                    ready_list = me->proc;
                    (me->proc)->link = NULL;

                    //traverse_ready_list();
                }
                
                /* display message */    
                printf( "SLEEP CODE: at time %d: waking %s up \n",            
                        get_current_time(), (me->proc)->id );

                //printf("---------------------------------------------------- \n");

                dispatch();

                // move on to next in waiting list
                MyPCB *temp = me;
                me = me->next;
                temp->next = NULL;
                temp->prev = NULL; 

                // how should I free memory ????
                kfree(temp);            
            }
            else {          
                // decrement current's process remaining time
                --(me->time_remain);

                // move on to next in waiting list
                me = me->next;            

            }
        }
        
    }
}


/*
 * returns number of processes that are currently waiting on sleep()
 * calls
 */
int num_sleeping (void) {

    int count=0;

    MyPCB *me = waiting_list_head;

    while (me != NULL) {
        count++;
        me = me->next;
    }
    return count;
}

void traverse_ready_list(void) {

    struct PCB *node = ready_list;

    printf("*********************** \n");
    printf("READY LIST \n");

    while (node != NULL) {
        printf("%s --> \n", node->id);
        node = node->link;
    }

    printf("*********************** \n");
}

